package com.concurrent;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @Classname CASDemo
 * @Description CAS
 * CAS:比较并交换 ==> compareAndSwap
 */
public class CASDemo {
    public static void main(String[] args) {
//        demo1();
//        aba();
        // ABA问题解决
        ReferenceData r1 = new ReferenceData("11", "value1");
        ReferenceData r2 = new ReferenceData("22", "value2");
        ReferenceData r3 = new ReferenceData("33", "value3");

        AtomicStampedReference<ReferenceData> reference = new AtomicStampedReference<>(r1, 1);
        new Thread(() -> {
            final int stamp = reference.getStamp();
            try {
                System.out.println("当前线程：" + Thread.currentThread().getName() + "开始，并sleep 5秒");
                TimeUnit.SECONDS.sleep(5); // sleep 5秒 等thread2执行
                System.out.println("当前线程：" + Thread.currentThread().getName() + "开始，sleep 5秒完成");
                System.out.println(reference.compareAndSet(r1, r3, stamp, stamp + 1));
                println(reference);
                System.out.println("当前线程：" + Thread.currentThread().getName() + "结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread1").start();
        new Thread(() -> {
            int stamp = reference.getStamp();
            System.out.println("当前线程：" + Thread.currentThread().getName() + "开始");
            System.out.println(reference.compareAndSet(r1, r2, stamp, stamp + 1));
            println(reference);
            stamp = reference.getStamp();
            System.out.println(reference.compareAndSet(r2, r1, stamp, stamp + 1));
            println(reference); // 版本号和当前版本号不一致，所以没有更新成功
            System.out.println("当前线程：" + Thread.currentThread().getName() + "结束");
        }, "thread2").start();
    }

    public static void println(AtomicStampedReference reference) {
        System.out.println("Thread.currentThread().getName()："+Thread.currentThread().getName()+"    当前reference值：" + reference.getReference()+"    当前版本："+reference.getStamp());
    }

    /**
     * ABA问题
     */
    private static void aba() {
        ReferenceData r1 = new ReferenceData("11", "value1");
        ReferenceData r2 = new ReferenceData("22", "value2");
        ReferenceData r3 = new ReferenceData("33", "value3");

        AtomicReference<ReferenceData> reference = new AtomicReference<>(r1);
        new Thread(() -> {
            try {
                System.out.println("当前线程：" + Thread.currentThread().getName() + "开始，并sleep 10秒");
                TimeUnit.SECONDS.sleep(10); // sleep十秒 等thread2执行
                System.out.println("当前线程：" + Thread.currentThread().getName() + "开始，sleep 10秒完成");
                System.out.println(reference.compareAndSet(r1, r3));
                print(reference);
                System.out.println("当前线程：" + Thread.currentThread().getName() + "结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread1").start();
        new Thread(() -> {
            System.out.println("当前线程：" + Thread.currentThread().getName() + "开始");
            System.out.println(reference.compareAndSet(r1, r2));
            print(reference);
            System.out.println(reference.compareAndSet(r2, r1));
            print(reference);
            System.out.println("当前线程：" + Thread.currentThread().getName() + "结束");
        }, "thread2").start();
    }

    public static void print(AtomicReference reference) {
        System.out.println("当前reference值：" + reference.get());
    }

    public static void demo1() {
        // 基本类型的原子引用类
        AtomicInteger atomicInteger = new AtomicInteger(0);

        System.out.println(atomicInteger.compareAndSet(0, 2020) + "\t curre data:" + atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(0, 100) + "\t curre data:" + atomicInteger.get());


        // 对象的的原子引用类
        ReferenceData r1 = new ReferenceData("1", "value1");
        ReferenceData r2 = new ReferenceData("2", "value2");
        ReferenceData r3 = new ReferenceData("3", "value3");

        AtomicReference<ReferenceData> reference = new AtomicReference<>(r1);
        System.out.println(reference.compareAndSet(r1, r2));
        System.out.println("当前reference值：" + reference.get());
        System.out.println(reference.compareAndSet(r1, r3));
        System.out.println("当前reference值：" + reference.get());
    }
}

class ReferenceData {
    private String id;
    private String value;

    public ReferenceData(String id, String value) {
        this.id = id;
        this.value = value;
    }

    @Override
    public String toString() {
        return "ReferenceData{" +
                "id='" + id + '\'' +
                ", value='" + value + '\'' +
                '}';
    }
}
